/* ActivityMonitorWidget.js
 *
 * Copyright 2006, Tim Dwelle.
 *
 * Licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.  See
 * the License for the specific language governing
 * permissions and limitations under the License.
 *
 */

$package("dowry.widget");

/**
 * ActivityMonitorWidget provides a client-side monitor of
 * session management, to prevent the user from timing out
 * on the server while performing work on the client.
 *
 * <p>
 * Whenever the user makes a keypress or moves the
 * mouse within the window, the component registers that the
 * user is active.  If the user has been active within the
 * window of a pre-specified duration, then the widget will
 * send a remote request to "ping" the server and keep the
 * server's session alive.
 * </p>
 *
 * <p>
 * If the user has been inactive for an extended period,
 * the widget can display a warning message to the user.  If
 * the user remains inactive, they may be redirected to a
 * login page when their session has timed out.
 * </p>
 *
 * <p>
 * This widget should be applied to a block element
 * (like a <code>&lt;span&gt;</code> or a <code>
 * &lt;div&gt;</code>) that is appropriately styled for
 * your desired warning message.  For instance, the
 * following CSS declaration places the warning message in
 * red in the upper right hand corner of the page.
 * </p>
 *
 * <pre>
 * .activitymonitor
 * {
 *    position         : absolute;
 *    background-color : red;
 *    color            : white;
 *    top              : 0px;
 *    right            : 0px;
 * }
 * </pre>
 *
 */
$class("ActivityMonitorWidget").$extends("Widget").$as(
{
    /**
     * Property representing the amount of time in seconds
     * between pings.  The default is 30 seconds.
     *
     */
    interval : 30,

    /**
     * Property representing the amount of time in seconds
     * before timing out the user and redirecting them.
     * This should usually be slightly smaller than the
     * server-side timeout.  The default is 1800 seconds
     * (30 minutes).
     *
     */
    timeout : 1800,

    /**
     * Property representing the URL to redirect the user to
     * when they have been  inactive beyond the timeout
     * duration.  The default is <code>http://www.google.com
     * </code>, but you will undoubteldy want to provide
     * a more appropriate value (usually the URL of your
     * application's login screen).
     *
     */
    url : "http://www.google.com",

    /**
     * Property representing the amount of time in seconds
     * <i>prior to the timeout</i> when the warning message
     * should be displayed.  The default is 120 seconds
     * (2 minutes).
     *
     */
    warnBefore : 120,

    /**
     * Property representing whether the user has been
     * active within the current time interval.
     *
     */
    _active : false,

    /**
     * Property representing the elapsed time in seconds
     * since the last ping was sent to the server.
     *
     */
    _elapsed : 0,

    /**
     * Hide the activity monitor when we first create the
     * widget.
     *
     */
    ActivityMonitorWidget : function(ele)
    {
        this.$super(arguments);
        this.hide();
    },

    /**
     * When the widget is started, set up the event
     * listeners for whenever the user makes a keypress or
     * moves the mouse within the window.  Also begin the
     * timer to check if we need to ping the server.
     *
     */
    start : function()
    {
        Dowry.event.addEvent(window,
                             "keypress",
                             this.setActive.callback(true));

        Dowry.event.addEvent(window,
                             "mousemove",
                             this.setActive.callback(true));

        window.setInterval(this.ping, this.interval * 1000);
    },

    /**
     * Sets whether the user has been active within this
     * current interval.
     *
     * @param b  a boolean indicating if the user has been
     *           active in the current interval or not
     *
     */
    setActive : function(b)
    {
        // if we are inactive, and are now active,
        // then hide the warning...
        if (!this._active && b)
        {
            this.hide();
        }

        this._active = b;
    },

    /**
     * The <code>ping()</code> method will ping the remote
     * server if the user has been active within the current
     * time interval.  If not, then <code>ping()</code>
     * simply increments the elapsed time counter.  If the
     * elapsed time exceeds the timeout period, then the
     * user will be redirected to the specified url.  If the
     * elapsed time falls within the warning period, then
     * the warning message will be shown.
     *
     */
    ping : function()
    {
        if (this._active)
        {
            this.setActive(false);
            this._elapsed = 0;
            Dowry.remote.validate();
        }
        else
        {
            this._elapsed += this.interval;

            // check for timeout
            if (this._elapsed >= this.timeout)
            {
                window.location = this.url;
            }

            // check if we need to show
            // the warning message
            else if (this._elapsed + this.warnBefore >= this.timeout)
            {
                this.show();
            }
        }
    },

    /**
     * Hides the widget's element by styling it to
     * display <code>none</code>.
     *
     */
    hide : function()
    {
        var ele = this.getElement();
        if (ele != null)
        {
            ele.style.display = "none";
        }
    },

    /**
     * Shows the widget's element by styling it to
     * display <code>block</code>.
     *
     */
    show : function()
    {
        var ele = this.getElement();
        if (ele != null)
        {
            ele.style.display = "block";
        }
    }
});